const qs = require('qs')
const comUtil = require('@/utils/common')

/*
路由方法封装
    1.集成:四种路由方法
            wx.redirectTo,
            wx.switchTab,
            wx.reLaunch,
            wx.navigateTo
    2.  根据参数类型进行剥离或者聚合
        对 query 利用 qs对象 支持 String Object 相互转化
    3. Promise 链式调用
示例
    1. $go({
        path: "/pages/login/login", // String 支持相对与绝对路径
        query: { key:"test" }, // Object | String
        method: "navigateTo" // String
    }).then((config)=>{
      // config -> { path , query , method } 路由配置
    })
    2. $go("/pages/login/login?key=test")

*/

function $go(urlProp) {
  return new Promise((resolve, reject) => {
    if (urlProp) {
      const formatUrlProp = {
        method: 'navigateTo'
      }
      // 序列化形参
      const urlPropType = comUtil.toType(urlProp)
      if (urlPropType === 'Object') {
        Object.assign(formatUrlProp, urlProp)
      } else if (urlPropType === 'String') {
        if (!urlPropType.trim()) {
          throw Error('urlProp is not a vaild String')
        }
        const [path, query] = urlProp.split('?')
        Object.assign(formatUrlProp, { path, query })
      } else {
        throw Error('urlProp type must be Object or String')
      }
      if (formatUrlProp.path) {
        const { path, query, method } = formatUrlProp
        let queryOfStr
        if (query) {
          const queryType = comUtil.toType(query)
          if (queryType === 'String') {
            queryOfStr = query
          } else if (queryType === 'Object') {
            queryOfStr = qs.stringify(query, { encode: false })
          } else {
            throw Error('router query type must be String or Object')
          }
        }
        let formatUrlPropToVaildStr = path
        if (queryOfStr && queryOfStr.trim()) {
          formatUrlPropToVaildStr += '?' + queryOfStr
        }
        wx[ method ]({
          url: formatUrlPropToVaildStr,
          success() {
            resolve(formatUrlProp)
          },
          fail(error) {
            reject(error)
          }
        })
      } else {
        throw Error('router path is not defined')
      }
    } else {
      throw Error('urlProp must be required')
    }
  })
}

/*
  // $go in Event
  示例
    <view
      bind:tap="$goEvent"
      data-path="/pages/user/user"
      data-query = "key=test"
      data-method= ""
    >
      to login
    </view>
*/
function $goEvent(event) {
  if (event) {
    const eventDataSet = event.currentTarget.dataset
    if (eventDataSet) {
      return $go(eventDataSet)
    } else {
      throw Error('Event dataset is not defined')
    }
  } else {
    throw Error('Event Object is not defined')
  }
}

/*
路由 -- 返回
方便在当前路由页面改变路由栈中其他页面内容
 1. 支持 Promise调用
 2. 对路由栈可查找
 3. 成功返回可获取返回页面实例
示例
    $back({
        leve: 1 , // Number  预期返回的层级 优先级大于 path
    //  path:"/pages/index/index" , // String  预期返回路由的绝对路径
    //  near: true , // Boolean 是否就近查找
    }).then(backPage=>{
        backPage 返回路由的Page 实例
      // console.log(backPage)
    })
注意
  leve 单纯向上回退路由栈的层级
  path 根据路由栈进行 path 匹配  确定返回的层级
  near 通常与 path 匹配使用 对路由栈进行正反序匹配
    考虑这样一个示例  路由栈:[ a , c , b , c , d ]
    b 需要返回 c
    near === true | d -> c
    near === false | d -> a -> c

*/

function $back(backProp) {
  return new Promise((resolve, reject) => {
    if (backProp && comUtil.isType(backProp, 'Object')) {
      backProp = Object.assign({ near: true }, backProp)
      let backRealyLeve
      let expectBackPage
      if (comUtil.hasOwn(backProp, 'leve')) {
        const { leve } = backProp
        if (Number.isSafeInteger(leve) && leve >= 1) {
          backRealyLeve = leve
          const pages = getCurrentPages()
          if (backRealyLeve <= pages.length - 1) {
            expectBackPage = pages[ pages.length - 1 - backRealyLeve ]
          } else {
            throw Error('backProp.leve greater than route stack length')
          }
        } else {
          throw Error('backProp.leve must be SafeInteger and >= 1')
        }
      } else if (backProp.path) {
        const { path } = backProp
        if (typeof path === 'string' && path.trim()) {
          const pages = getCurrentPages()
          const nextMayReversePages = pages.slice(0, pages.length - 1)
          if (!nextMayReversePages.length) {
            throw Error('There is no route stack to return')
          }
          if (backProp.near === false) {
            nextMayReversePages.reverse()
          }
          const waitFindPage = nextMayReversePages.find((pageItem, pageIndex) => {
            const pageItemAbsPath = '/' + pageItem.route
            if (pageItemAbsPath === path) {
              backRealyLeve = backProp.near ? pageIndex : nextMayReversePages.length - pageIndex - 1
              return true
            }
          })
          if (!waitFindPage) {
            throw Error(`In pages not find ${path}`)
          }
          expectBackPage = waitFindPage
        } else {
          throw Error('backProp.url must be a vaild string')
        }
      } else {
        throw Error('Missing required params in $back')
      }
      // 调用原生方法
      wx.navigateBack({
        delta: backRealyLeve,
        success() {
          resolve(expectBackPage)
        },
        fail(error) {
          reject(error)
        }
      })
    } else {
      throw Error('backProp must be a vaild Object')
    }
  })
}

/*
  $back in Event
  示例
    <view
      bind:tap = "$backEvent"
      data-leve = "{{ 1 }}"
      data-path ="/pages/index/index"
      data-near = "{{ true }}"
    >
      back index
    </view>
*/

function $backEvent(event) {
  if (event) {
    const eventDataSet = event.currentTarget.dataset
    if (eventDataSet) {
      return $back(eventDataSet)
    } else {
      throw Error('Event dataset is not defined')
    }
  } else {
    throw Error('Event Object is not defined')
  }
}

module.exports = {
  $go,
  $goEvent,
  $back,
  $backEvent
}
